package com.haixee.parkdemo.data_acl.mybatis;


import com.haixee.parkdemo.common.context.WebContextHolder;
import com.haixee.parkdemo.common.entity.AuthData;
import com.haixee.parkdemo.data_acl.mybatis.service.DataAclService;
import lombok.Data;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.sql.Connection;

//@Intercepts(@Signature(type = Executor.class, method = "query",
//        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
@Data
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataAclInterceptor implements Interceptor {


    private DataAclService dataAclService;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        if (!DataAclHolder.getProcess()) {
            return invocation.proceed();
        }

        try {

            AuthData auth = WebContextHolder.getAuth();

            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            //通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性;：MetaObject是Mybatis提供的一个用于方便、
            //优雅访问对象属性的对象，通过它可以简化代码、不需要try/catch各种reflect异常，同时它支持对JavaBean、Collection、Map三种类型对象的操作。
            MetaObject metaObject = MetaObject
                    .forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                            new DefaultReflectorFactory());
            //先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
            //id为执行的mapper方法的全路径名，如com.uv.dao.UserMapper.insertUser
            String id = mappedStatement.getId();
            //sql语句类型 select、delete、insert、update
            String sqlCommandType = mappedStatement.getSqlCommandType().toString();
            //数据库连接信息
//        Configuration configuration = mappedStatement.getConfiguration();
//        ComboPooledDataSource dataSource = (ComboPooledDataSource)configuration.getEnvironment().getDataSource();
//        dataSource.getJdbcUrl();

            BoundSql boundSql = statementHandler.getBoundSql();
            //获取到原始sql语句
            String sql = boundSql.getSql();
            String mSql = getSql(sql, auth);
            //通过反射修改sql语句
            Field field = boundSql.getClass().getDeclaredField("sql");
            field.setAccessible(true);
            field.set(boundSql, mSql);
        } finally {
            DataAclHolder.remove();
        }

        return invocation.proceed();
    }

//    public Object processParameter(MappedStatement ms, Map<String, Object> paramMap, BoundSql boundSql, CacheKey pageKey) {
//
//        //处理pageKey
//        pageKey.update(page.getStartRow());
//        pageKey.update(page.getPageSize());
//        //处理参数配置
//        if (boundSql.getParameterMappings() != null) {
//            List<ParameterMapping> newParameterMappings = new ArrayList<ParameterMapping>(boundSql.getParameterMappings());
//            if (page.getStartRow() == 0) {
//                newParameterMappings.add(new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_SECOND, Integer.class).build());
//            } else {
//                newParameterMappings.add(new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_FIRST, Integer.class).build());
//                newParameterMappings.add(new ParameterMapping.Builder(ms.getConfiguration(), PAGEPARAMETER_SECOND, Integer.class).build());
//            }
//            MetaObject metaObject = MetaObjectUtil.forObject(boundSql);
//            metaObject.setValue("parameterMappings", newParameterMappings);
//        }
//        return paramMap;
//    }


    public String getSql(String sql, AuthData authData) {

        StringBuilder sqlBuilder = new StringBuilder(sql.length());
        sqlBuilder.append(sql);

        if (authData.getTenantId() != 0) {
            sqlBuilder.append(" and tenant_id = ");
            sqlBuilder.append(authData.getTenantId());
        }

        if (authData.getRoleScope() == 3) {
            return sqlBuilder.toString();
        }


        sqlBuilder.append(" and (");

        if (authData.getRoleScope() == 1) {
            sqlBuilder.append(" opt_uid = ");
            sqlBuilder.append(authData.getUid());

            if (authData.getUnderDeptList().size() != 0) {
                sqlBuilder.append(" or ");

                StringBuilder optJobBuilder = new StringBuilder("opt_dept in (");
                for (int i = 0; i < authData.getUnderDeptList().size(); i++) {
                    optJobBuilder.append(authData.getUnderDeptList().get(i));
                    if (i != authData.getUnderDeptList().size() - 1) {
                        optJobBuilder.append(",");
                    }
                }
                optJobBuilder.append(")");

                sqlBuilder.append(optJobBuilder);
            }

        } else if (authData.getRoleScope() == 2) {

            if (authData.getUnderDeptList().size() != 0) {

                StringBuilder optJobBuilder = new StringBuilder("opt_dept in (");
                for (int i = 0; i < authData.getUnderDeptList().size(); i++) {
                    optJobBuilder.append(authData.getUnderDeptList().get(i));
                    if (i != authData.getUnderDeptList().size() - 1) {
                        optJobBuilder.append(",");
                    }
                }
                optJobBuilder.append(")");

                sqlBuilder.append(optJobBuilder);
            }

        }

        sqlBuilder.append(")");


        return sqlBuilder.toString();
    }

}
